﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;

namespace XGameLibrary.Sprite
{


    public class Animation : ICloneable
    {
        public enum Direction { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest }
        public enum State
        {
            Stand, Run, Combat, Magic, Die
        }

        public static Direction GetDirectionByTan(Vector2 from, Vector2 to)
        {
            double currentX = from.X;
            double currentY = from.Y;
            double targetX = to.X;
            double targetY = to.Y;
            double tan = (targetY - currentY) / (targetX - currentX);
            if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY <= currentY)
            {
                return Direction.North;
            }
            else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY < currentY)
            {
                return Direction.NorthEast;
            }
            else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX >= currentX)
            {
                return Direction.East;
            }
            else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX > currentX && targetY > currentY)
            {
                return Direction.SouthEast;
            }
            else if (Math.Abs(tan) >= Math.Tan(Math.PI * 3 / 8) && targetY >= currentY)
            {
                return Direction.South;
            }
            else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY > currentY)
            {
                return Direction.SouthWest;
            }
            else if (Math.Abs(tan) <= Math.Tan(Math.PI / 8) && targetX <= currentX)
            {
                return Direction.West;
            }
            else if (Math.Abs(tan) > Math.Tan(Math.PI / 8) && Math.Abs(tan) < Math.Tan(Math.PI * 3 / 8) && targetX < currentX && targetY < currentY)
            {
                return Direction.NorthWest;
            }
            else
            {
                return Direction.North;
            }
        }

        public static int DirectionCount
        {
            get { return System.Enum.GetNames(typeof(Direction)).Length; }
        }
        public const int FrameCount = 23;
        #region Field Region

        int framesPerSecond;
        TimeSpan frameLength;
        TimeSpan frameTimer;
        int currentFrame;
        int frameWidth;
        int frameHeight;
        State aniState;

        #endregion

        #region Property Region

        public int StateMin
        {
            get
            {
                switch (aniState)
                {
                    case State.Stand:
                        return 0;
                    case State.Run:
                        return 3;
                    case State.Combat:
                        return 9;
                    case State.Magic:
                        return 14;
                    case State.Die:
                        return 20;
                    default:
                        return 0;
                }
            }
        }
        public int StateMax
        {
            get
            {
                switch (aniState)
                {
                    case State.Stand:
                        return 2;
                    case State.Run:
                        return 8;
                    case State.Combat:
                        return 13;
                    case State.Magic:
                        return 19;
                    case State.Die:
                        return 22;
                    default:
                        return 2;
                }
            }
        }

        public State AniState
        {
            get { return this.aniState; }
            set 
            {
                if (this.aniState != value)
                {
                    this.aniState = value;
                    this.currentFrame = this.StateMin;
                }
            }
        }

        public int FramesPerSecond
        {
            get { return framesPerSecond; }
            set
            {
                if (value < 1)
                    framesPerSecond = 1;
                else if (value > 60)
                    framesPerSecond = 60;
                else
                    framesPerSecond = value;
                frameLength = TimeSpan.FromSeconds(1 / (double)framesPerSecond);
            }
        }

        public int CurrentFrame
        {
            get { return currentFrame; }
        }

        public int FrameWidth
        {
            get { return frameWidth; }
        }

        public int FrameHeight
        {
            get { return frameHeight; }
        }

        #endregion

        #region Constructor Region

        public Animation(int frameWidth, int frameHeight)
        {
            this.frameWidth = frameWidth;
            this.frameHeight = frameHeight;

            FramesPerSecond = 5;
            Reset();
        }

        private Animation(Animation animation)
        {
            FramesPerSecond = 5;
        }

        #endregion

        #region Method Region

        public void Update(GameTime gameTime)
        {
            frameTimer += gameTime.ElapsedGameTime;

            if (frameTimer >= frameLength)
            {
                frameTimer = TimeSpan.Zero;
                currentFrame = (currentFrame + 1) > this.StateMax ? this.StateMin : currentFrame + 1;
            }
        }

        public void Reset()
        {
            this.aniState = State.Stand;
            currentFrame = 0;
            frameTimer = TimeSpan.Zero;
        }

        #endregion

        #region Interface Method Region

        public object Clone()
        {
            Animation animationClone = new Animation(this);

            animationClone.frameWidth = this.frameWidth;
            animationClone.frameHeight = this.frameHeight;
            animationClone.Reset();

            return animationClone;
        }

        #endregion
    }
}
